#include "QROHost_P.h"

static void onPropertyChanged(QROHost::Private *_P,ObjectID target, int index);
static void markPropertyModifyImp(QROHost::Private* _P,int id,int index);
static QVariantList packPropertyUpdateData(QROHost::Private* _P,const PropertyModifyDataSet& props);
static void dispatchPropertyMark(QROHost::Private* _P,int id, int prop, QVariant value);

//对象属性监视器
class PropertyObserver:public QtPrivate::QSlotObjectBase
{
public:
    PropertyObserver():QtPrivate::QSlotObjectBase(&PropertyObserver::FuncImpl){

    }
    static void FuncImpl(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret){
        auto This = static_cast<PropertyObserver*>(this_);

        if(which == QtPrivate::QSlotObjectBase::Operation::Call)
        {
            onPropertyChanged(This->_P,This->id,This->prop);
        }
        else if(which == QtPrivate::QSlotObjectBase::Compare)
        {
            *ret = false;
        }
        else if(which == QtPrivate::QSlotObjectBase::Destroy)
        {
            delete This;
        }
    }

    QROHost::Private* _P;
    ObjectID id;
    int prop;
};

void QROHost::Private::onUpdateProperty(){
    auto packet = packPropertyUpdateData(this,m_propUpdateQueue);
    m_propUpdateQueue.clear();
    if(packet.empty())
    {
        return;
    }
    auto data = compressVariant(packet);

    for(auto i:m_channels)
    {
        i.second->channel->propertyUpdate(data);
    }
}

void QROHost::Private::observeProperty(const QMetaObject* meta,ObjectData* data,QObject* target,int prop){
    auto property = meta->property(prop);
    if(property.hasNotifySignal())
    {
        auto notifyIndex = property.notifySignalIndex();
        auto observer = new PropertyObserver();
        observer->id = data->id;
        observer->prop = prop;
        observer->_P = this;

        auto connection = QObjectPrivate::connect(target,notifyIndex,_Q,observer,Qt::AutoConnection);
        data->connections << connection;//记录下监控器的连接，等着
    }
}

void QROHost::Private::clearPropertyQueue(QList<ObjectID> obj)
{
    PropertyModifyDataSet temp = m_propUpdateQueue;

    QSet<ObjectID> objSet{obj.begin(),obj.end()};


    for(auto i:m_propUpdateQueue)
    {
        auto id = std::get<0>(i);
        if(objSet.contains(id))
        {
            temp.erase(i);
        }
    }

    m_propUpdateQueue = temp;
}

QVariantList QROHost::Private::packAllPropertyData()
{
    PropertyModifyDataSet set;

    for(auto i:m_objectDataMap)
    {
        auto obj = i->obj;

        auto meta = obj->metaObject();
        auto m = QRemoteObject2Meta::Instance()->fetchRemoteClass(meta->className());
        for(const auto& index : QRemoteObject2Meta::Instance()->allPropData(meta->className()))
        {
            set.insert(PropertyModifyData(i->id,index));
        }
    }

    return packPropertyUpdateData(this,set);
}

void QROHost::Private::onMarkPropertyModify(QObject *o, int index){
    if(!QRemoteObject2Meta::Instance()->allPropData(o->metaObject()->className()).contains(index)) return;

    if(m_objectIdMap.count(o)) return;

    markPropertyModifyImp(this,m_objectIdMap[o],index);
}

void QROHost::Private::onPushProperty(RemoteInterfaceSourceImp* src,int id, int prop, QVariant value)
{
    auto data = m_objectDataMap[id];

    if(!data)
    {
        return;
    }

    auto meta = data->obj->metaObject();
    auto metaProp = meta->property(prop);
    auto roProp = QRemoteObject2Meta::Instance()->findPropData(meta->className(),prop);
    if(roProp.index == -1) return;

    if(roProp.flag != QRemoteObject2::ROPropertyFlag::ReadWrite) return;
    //对客户端传过来的数据进行网络解码
    valueFromNet(value,metaProp.typeId());
    dispatchPropertyMark(this,id,prop,value);
}

void QROHost::Private::onPushProperty(QObject *target,int prop,QVariant value){
    if(!m_objectIdMap.count(target)) return;
    auto id = m_objectIdMap[target];
    dispatchPropertyMark(this,id,prop,value);
}

static void onPropertyChanged(QROHost::Private *_P,ObjectID target, int index)
{
    markPropertyModifyImp(_P,target,index);
}

//标记某属性修改了
static void markPropertyModifyImp(QROHost::Private* _P,int id,int index)
{
    PropertyModifyData data(id,index);
    _P->m_propUpdateQueue.insert(data);
    _P->_Q->propertyMarked(_P->m_objectDataMap[id]->obj,index);
}

static QVariantList packPropertyUpdateData(QROHost::Private* _P,const PropertyModifyDataSet& props){
    QVariantList ret;
    for(const auto &i:props)
    {
        ObjectID obj;
        int index;
        std::tie(obj,index) = i;

        auto data = _P->m_objectDataMap[obj];
        auto object = data->obj;
        auto meta = object->metaObject();
        auto prop = meta->property(index);

        auto value = prop.read(object);

        _P->valueToNet(value);

        QVariant singledata = QVariantList{
            QVariant::fromValue(obj),
            QVariant::fromValue(index),
            value
        };
        ret << singledata;
    }

    return ret;
}

static void dispatchPropertyMark(QROHost::Private* _P,int id, int prop, QVariant value)
{
    auto data = _P->m_objectDataMap[id];
    auto meta = data->obj->metaObject();
    auto metaProp = meta->property(prop);
    if(metaProp.hasNotifySignal())//有notifier的仅设置值
    {
        metaProp.write(data->obj,value);
    }
    else{//无notifier的需要设置值后打标记
        metaProp.write(data->obj,value);
        markPropertyModifyImp(_P,id,prop);
    }
}
